#ifndef _TCP_NETWORK_HPP
#define _TCP_NETWORK_HPP

#include <vector>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <strings.h>
#include <errno.h>
#include <boost/shared_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/thread.hpp>
#include "../util/Role.hpp"
#include "../membership/Membership.hpp"
#include "../Parameters.hpp"
#include "../constants.hpp"


namespace BFT
{
  namespace network
  {
    typedef struct
    {
      //a server socket listener
      int listenerSocket;
      //the address to bind the socket to
      struct sockaddr_in socketAddress;		
    } listenerInfo;
			
    typedef struct
    {
      // interface index in the vector of principals returned by membership.getmyinterfaces
      int interfaceIndex;
      //and the file descriptor of the accepted connection
      int socketFd;
      //address of the remote peer
      sockaddr remote_address;
      //size of the address
      socklen_t addrlen;
      //shared pointer to a buffer of bytes read from the socket, caches partial messages
      boost::shared_ptr<char> bytes;
      //number of bytes that are in the buffer right now
      int bytes_size;
      //length of the complete message
      int message_length; // in host endianness!!

				
      // bytes.use_count() != 0 IFF bytes.get() points to a cached partial message
      // bytes.use_count() != 0 IFF message_length == length of the message to be read
      // bytes.use_count() == 0 IFF message_length == -1
				
    } socketInfo;
			
    class TCPNetwork
    {

    public:
				
      TCPNetwork(BFT::util::Role _role, boost::shared_ptr<BFT::membership::Membership> _membership);
      ~TCPNetwork();
				
      // select from both the listening and the opened sockets. handle transparently new connection requests
      // and return as soon as at least a complete message has been read.
      std::vector< boost::tuple< int , boost::shared_ptr<char> , int > >selectMessages();
      //			   ^^^	                   ^^^^    ^^^ 
      //		   interface index                 bytes   size (# of bytes)

				
      BFT::util::Role getMyRole();
				
      void start();
      void stop();
      void waitForListening();
				
    protected:
				
      void acceptConnection(int _listener);
				
      // readMessage returns *1* *complete* messages from the socket _socket.
      //
      // The bytes of the message are wrapped in a shared_ptr (to avoid memory copy of potentially
      // large buffers). Each message is also characterized by two integers describing:
      // - the index of the interface from which the message has been received (that is, the
      //   index of the corresponding principal in the vector retirned by membership::getmyinterfaces())
      // - the size of the buffer (number of bytes)
      //
      // index, bytes and size are wrapped inside a tuple, representing a single message.
      // if it has not been possible to read a complete message, a tuple is returned in which
      // size = 0
      boost::tuple< int , boost::shared_ptr<char> , int > readMessage(int _socket);
      //            ^^^                     ^^^^    ^^^
      //     interface index                bytes   size (# of bytes)
				

      // readMessages returns *all* the *complete* messages
      // that it has been possible to read from the socket _socket, up to _max.
      //
      // The bytes of the message are wrapped in a shared_ptr (to avoid memory copy of potentially
      // large buffers). Each message is also characterized by two integers describing:
      // - the index of the interface from which the message has been received (that is, the
      //   index of the corresponding principal in the vector retirned by membership::getmyinterfaces())
      // - the size of the buffer (number of bytes)
      //
      // index, bytes and size are wrapped inside a tuple, representing a single message.
      // 
      // multiple messages are packed in the vector of tuples returned by readMessages.
      // if no messages have been read, a vector of size 0 is returned
      std::vector< boost::tuple< int , boost::shared_ptr<char> , int > >readMessages(int _socket, int _max);
      //                         ^^^                     ^^^^    ^^^
      //		   interface index                bytes   size (# of bytes)


    private:
				
      //vector of sockets on which we will accept connections
      std::vector<listenerInfo> listenerSockets;
				
      //hash table that associate a listener to the interface index in 
      // the vector of principals returned by membership.getmyinterfaces
      std::map<int, int> listenerIndex; // key: listener, value: interface index
				
      //hash table that associate the fd of an accepted connection to the
      // related information
				
      std::map<int, socketInfo> socketTable; // key: socket, value: socketInfo
				
      std::vector<int> socketToErase;//vector of sockets to be erased from socketTables as soon as it is safe to do so
				
      BFT::util::Role myRole;
      int myid;
      boost::shared_ptr<BFT::membership::Membership> membership;
      bool listening;
      bool justaccepted;
      struct timeval timeout;
				
				
      fd_set rdfs; //set of file descriptor to read from
      int max_fd; //highest file descriptor checked by select()
				
								
      boost::mutex condition_mutex;
      boost::condition_variable listening_condition;
				
				
    }; //class TCPNetwork
		
    inline BFT::util::Role TCPNetwork::getMyRole(){return myRole;}
			
  }//namespace network
}//namespace BFT

#endif

